package com.caishi.lkx.exam.api;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.SecureUtil;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.caishi.lkx.common.AclResultCode;
import com.caishi.lkx.common.BaseApi;
import com.caishi.lkx.common.EncryptionApi;
import com.caishi.lkx.common.role.Roles;
import com.caishi.lkx.exam.model.*;
import com.caishi.lkx.exam.service.*;
import com.caishi.lkx.exam.service.impl.CoreQuestionCollectionService;
import com.caishi.lkx.exam.vo.PaperResultVo;
import com.caishi.lkx.exam.vo.SpecialConditionVo;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.zzw.common.Fetch;
import com.zzw.common.Wrappers;
import com.zzw.common.annotation.RateLimiterApi;
import com.zzw.common.entity.impl.BSDAbsEntity;
import com.zzw.common.entity.impl.BSDEntity;
import com.zzw.common.entity.impl.BaseEntity;
import com.zzw.common.exception.BizException;
import com.zzw.common.exception.BizRuntimeException;
import com.zzw.common.gateway.limit.RateLimiterApiTarget;
import com.zzw.common.qps.RejectStrategy;
import com.zzw.common.utils.MD5Util;
import com.zzw.common.utils.TimeUtil;

import com.caishi.lkx.exam.ExamResultCode;
import com.caishi.lkx.exam.ienum.type.PaperRecordType;
import com.caishi.lkx.exam.ienum.type.QuestionCollectionType;
import com.caishi.lkx.exam.model.re.ExColumnConfigReModel;
import com.caishi.lkx.exam.records.RecordsCollectType;
import com.caishi.lkx.exam.records.RecordsGetDataService;
import com.caishi.lkx.exam.records.dto.CollectionErrorAndCollectRecordDataDto;
import com.caishi.lkx.exam.records.dto.QuestionRecordsDto;

import com.caishi.lkx.exam.util.QuestionUtil;

import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.Parameters;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.extern.slf4j.Slf4j;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.*;

import javax.annotation.Resource;
import java.nio.charset.StandardCharsets;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Stream;

@Tag(name = "试卷记录相关接口")
@Slf4j
@RestController
@RequestMapping("/exam/paper-records")
public class PaperRecordsApi extends BaseApi {

    @Resource
    private IPaperRecordsService paperRecordsService;

    @Resource
    private IPaperService paperService;

    @Resource
    private ObjectMapper objectMapper;

    @Resource
    private RecordsGetDataService recordsGetDataService;

    @Resource
    private IChapterService chapterService;

    @Resource
    private IQuestionService questionService;

    @Resource
    private IQuestionTypeService questionTypeService;

    @Resource
    private IColumnService columnService;

    @Resource
    private IExColumnConfigReService columnConfigReService;

    @Resource
    private IPaperRuleService paperRuleService;

    @Resource
    private ICategoryService categoryService;

    @Resource
    private CoreQuestionCollectionService coreQuestionCollectionService;

    @Resource
    private IExamService examService;
    @Resource
    private IIndustryService industryService;

    private static final String SHARE_AES_KEY = "keyabbclaccdshcbsdhbanmkanbvsjjabxbsbsddasvdd";


    @Operation(summary = "获取记录详情 不包含试题信息")
    @GetMapping("/detail/{id}")
    @Roles
    public PaperRecordsModel detail(@PathVariable String id) {
        PaperRecordsModel model = paperRecordsService.getById(id);
        if (null != model && model.getUserId().equals(userContext.currentUserId())) {
            if (StrUtil.isNotBlank(model.getPaperId())) {
                PaperModel paperInfo = paperService.getPaperInfo(model.getPaperId());
                CategoryModel categoryModel = categoryService.getById(paperInfo.getCategoryId());
                paperInfo.setCategoryName(categoryModel.getName());
                paperInfo.setExamId(categoryModel.getExamId());
                paperInfo.setExamName(examService.queryName(categoryModel.getExamId()));
                paperInfo.setIndustryId(categoryModel.getIndustryId());
                paperInfo.setIndustryName(industryService.queryName(categoryModel.getIndustryId()));
                if (null != paperInfo) {
                    model.setExamTime(paperInfo.getExamTime());
                }
            }
            return model;
        }
        return null;
    }

    @Operation(summary = "获取试卷记录")
    @GetMapping("/list")
    public List<PaperRecordsModel> list(String id, @RequestParam RecordsCollectType recordsCollectType) {
        return paperRecordsService.selectList(paperRecordsService.wrappers()
                .eq(StrUtil.isNotEmpty(id), PaperRecordsModel::getPaperId, id)
                .eq(recordsCollectType.type2PaperRecordType() != null, PaperRecordsModel::getType, recordsCollectType.type2PaperRecordType())
                .eq(PaperRecordsModel::getUserId, userContext.currentUserId())
                .orderByDesc(PaperRecordsModel::getStartTime)
        );
    }

    @Operation(summary = "获取试卷做题次数")
    @GetMapping("/count/{id}")
    public Long count(@PathVariable String id) {
        return paperRecordsService.selectCount(paperRecordsService.wrappers()
                .eq(PaperRecordsModel::getUserId, userContext.currentUserId())
                .eq(PaperRecordsModel::getPaperId, id)
                //.isNotNull(PaperRecordsModel::getEndTime)
                .eq(PaperRecordsModel::getType, PaperRecordType.paperLibrary)
        );
    }

    @Operation(summary = "获取科目下完成试卷套数")
    @GetMapping("/finish-paper-t")
    public int finishPaperCount(@RequestParam Long categoryId) {
        return paperRecordsService.userFinishPaperCount(userContext.currentUserId(), categoryId);
    }


    @Operation(summary = "获取记录列表")
    @Parameters({
            @Parameter(name = "noFinish", description = "试卷是否完成"),
    })
    @GetMapping("/page")
    public IPage<PaperRecordsModel> page(@ModelAttribute PaperRecordsModel model, @ModelAttribute Page<PaperRecordsModel> page, Boolean noFinish) {
        model.setUserId(userContext.currentUserId());
        return paperRecordsService.page(page, Wrappers.lambdaQuery(model)
                .isNull(Boolean.FALSE.equals(noFinish), PaperRecordsModel::getEndTime)
                .isNotNull(Boolean.TRUE.equals(noFinish), PaperRecordsModel::getEndTime)
        );
    }


    @PostMapping("/generate-paper-records")
    @Parameters({
            @Parameter(name = "paperId", description = "试卷id", required = true),
            @Parameter(name = "strict", description = "强制创建"),
            @Parameter(name = "result", description = "是否返回生成试卷的试题集合"),
            @Parameter(name = "practiceModel", description = "是否时试卷练习模式"),
    })
    @Operation(summary = "试卷库创建试卷记录")
    @Transactional(rollbackFor = Exception.class)
    @EncryptionApi
    public Object generatePaperRecords(
            @RequestParam String industryCode,
            @RequestParam String examId,
            @RequestParam Long categoryId,
            String paperId,
            @RequestParam(required = false) String recordsId,
            @RequestParam String columnId,
            @RequestParam(defaultValue = "true") Boolean result,
            @RequestParam(defaultValue = "false") Boolean strict,
            @RequestParam(defaultValue = "false") Boolean practiceModel
    ) throws BizException {
        String userId = userContext.currentUserId();
        PaperRecordsModel records = null;
        var ct = userContext.currentTokenData().getClientType();
        if (StrUtil.isNotBlank(recordsId)) {
            records = paperRecordsService.getById(recordsId);
            if (!userId.equals(records.getUserId())) throw new BizRuntimeException(AclResultCode.noPower);
        } else {
            var reData = columnConfigReService.configData(columnId, paperId);

//            reData.setIsFree(true);
            if (reData == null || !reData.getIsFree() && !columnService.currentHaveAuth(userContext.currentGroupId(), userContext.currentLocalGroupId(), userContext.currentUserId(), categoryId, columnId, ct)) {
                throw new BizRuntimeException(AclResultCode.noPower);
            }
        }
        Map<String, Object> res = new HashMap<>();
        if (records == null && practiceModel) {
            records = paperRecordsService.selectOne(
                    paperRecordsService.wrappers()
                            .eq(PaperRecordsModel::getUserId, userId)
                            .eq(PaperRecordsModel::getPaperId, paperId)
                            .eq(PaperRecordsModel::getType, PaperRecordType.chapterPractice)
            );
        } else if (records == null && !strict) {
            records = paperRecordsService.userNotFinishPaperRecords(userId, PaperRecordType.paperLibrary, paperId);
            if (records != null && records.getType().equals(PaperRecordType.paperLibrary))
                records.setExamTime(paperService.getPaperInfo(records.getPaperId()).getExamTime());
        }
        res.put("create", records == null && !practiceModel);
        if (records == null) {
            var model = paperService.getPaperInfo(paperId);
            records = PaperRecordsModel.builder()
                    .paperId(paperId)
                    .type(practiceModel ? PaperRecordType.chapterPractice : PaperRecordType.paperLibrary)
                    .userId(userId)
                    .name(model.getName())
                    .examId(examId)
                    .categoryId(categoryId)
                    .startTime(LocalDateTime.now())
                    .allScore(model.getTotalScore().floatValue())
                    .passScore(model.getPassScore().floatValue())
                    .examTime(model.getExamTime())
                    .qcount(model.getQcount())
                    .build();
            records.setContent(model.getContent());
            paperService.addNewStudent(paperId);
            PaperRecordsModel paperRecordsModel = paperRecordsService.selectFirst(
                    Wrappers.<PaperRecordsModel>lambdaQuery()
                            .eq(PaperRecordsModel::getUserId, userId)
                            .eq(PaperRecordsModel::getPaperId, paperId)
                            .eq(PaperRecordsModel::getType, PaperRecordType.paperLibrary)
                            .isNotNull(PaperRecordsModel::getCompleteCount)
                            .orderByDesc(PaperRecordsModel::getCompleteCount));
            int count = 0;
            model.setStuCount(model.getStuCount()==null?1:model.getStuCount() + 1);
            paperService.update(model);
            if (null != paperRecordsModel) {
                if(strict){
                    paperRecordsModel.setEndTime(LocalDateTime.now());
                    paperRecordsService.update(paperRecordsModel);
                }
                count = null != paperRecordsModel.getCompleteCount() ? paperRecordsModel.getCompleteCount() : 0;
            }
            records.setCompleteCount(count);
            records = paperRecordsService.insert(records);
        }
        records.setContent(null);
        return records2Result(records, result, userId, industryCode, examId, categoryId);
    }

    @PostMapping("/public-generate-paper-records")
    @Operation(summary = "试卷库创建试卷记录")
    @Transactional(rollbackFor = Exception.class)
    @EncryptionApi
    @Roles
    public Map<Object, Object> generatePaperRecords(
            @RequestParam String industryCode,
            @RequestParam String examId,
            @RequestParam Long categoryId,
            @RequestParam String paperId,
            @RequestParam String columnId
    ) throws BizException {
        var localGroupId = userContext.currentLocalGroupId();
        var ct = userContext.currentTokenData().getClientType();
        var reData = columnConfigReService.configData(columnId, paperId);
        if (reData == null || !reData.getIsFree() && !columnService.currentHaveAuth(null, localGroupId, null, categoryId, columnId, ct)) {
            throw new BizRuntimeException(AclResultCode.noPower);
        }
        var model = paperService.getPaperInfo(paperId);
        var records = PaperRecordsModel.builder()
                .paperId(paperId)
                .type(PaperRecordType.paperLibrary)
                .name(model.getName())
                .examId(examId)
                .categoryId(categoryId)
                .startTime(LocalDateTime.now())
                .allScore(model.getTotalScore().floatValue())
                .passScore(model.getPassScore().floatValue())
                .examTime(model.getExamTime())
                .build();
        records.setContent(model.getContent());
        records.setId("public");
        paperService.addNewStudent(paperId);
        var data = coreQuestionCollectionService.safeProcess(
                new QuestionRecordsDto(),
                new CollectionErrorAndCollectRecordDataDto(),
                coreQuestionCollectionService.clientCollectionData(records, industryCode, examId, categoryId),
                localGroupId == null ? null : localGroupId.toString(),
                records.getId(),
                false
        );
        data.setRecordsType(RecordsCollectType.paperRecordPaperLibrary);
        data.setType(QuestionCollectionType.paper);
        return MapUtil.builder()
                .put("data", data)
                .put("mode", records)
                .build();
    }

    @PostMapping("/generate-random-records")
    @Operation(summary = "生成随机试卷记录")
    @EncryptionApi
    @Roles
    @RateLimiterApi(namespace = "exam:safe-generate:", maxRate = 2, recoveryCount = 2, millisecond = 2000, target = RateLimiterApiTarget.user, rejectStrategy = RejectStrategy.throw_exception)
    public Object generateRandomPaperRecords(
            @RequestParam Long categoryId,
            @RequestParam String columnId,
            @RequestParam(defaultValue = "true") Boolean result
    ) throws BizException {
        String userId = userContext.currentUserId();
        //var localGroupId = userContext.currentLocalGroupId();
        var category = categoryService.getById(categoryId);
        var ct = userContext.currentTokenData().getClientType();


        var records = PaperRecordsModel.builder()
                .type(PaperRecordType.randomFormPaper)
                .userId(userId)
                .name(generateName(null, null, PaperRecordType.randomFormPaper, userId))
                .examId(category.getExamId())
                .categoryId(categoryId)
                .startTime(LocalDateTime.now())
                .build();
        // 获取规则对象初始化
        PaperRuleModel rule = null;
        // 根据栏目ID查询对应的配置记录
        var re = columnConfigReService.selectFirst(columnConfigReService.wrappers()
                .eq(ExColumnConfigReModel::getColumnId, columnId)
                .orderByAsc(ExColumnConfigReModel::getSort)
        );
        // 如果查询到配置记录，则根据配置ID获取规则
        if (re != null) rule = paperRuleService.getById(re.getConfigId());
        // 如果未获取到规则，则尝试根据分类ID获取默认规则
        if (rule == null) rule = paperRuleService.getCategoryRule(categoryId);
        // 如果仍然未获取到规则，则抛出异常
        if (rule == null) throw new BizRuntimeException("组卷规则不存在");
        // 检查栏目配置是否为免费，以及用户是否有权限访问
        boolean b4 = !columnConfigReService.configIsFree(columnId, rule.getId());
        if (!columnService.currentHaveAuth(userContext.currentGroupId(), null, userId, categoryId,columnId, ct) && b4) {
            throw new BizRuntimeException(AclResultCode.noPower);
        }

        // 生成试题区块
        generateQuestionBlock(categoryId, records, paperRuleService.rule2vo(rule), rule.getQuestionTotal(), null, "随机组卷",userContext.loginStatus());

        // 设置记录的总分和及格分
        //records.setPassScore(records.getAllScore() * (rule.getPassScore() / rule.getTotalScore().floatValue()));
        records.setAllScore(Float.valueOf(rule.getTotalScore()));
        records.setPassScore(Float.valueOf(rule.getPassScore()));
        records.setRemark(rule.getRemark());
        records.setPaperId(rule.getId());
        // 根据用户登录状态处理记录
        if (userContext.loginStatus()) {
            records = paperRecordsService.insert(records);
        } else {
            records.setId("public");
            var data = coreQuestionCollectionService.safeProcess(
                    new QuestionRecordsDto(),
                    new CollectionErrorAndCollectRecordDataDto(),
                    coreQuestionCollectionService.clientCollectionData(records, category.getIndustryId(), category.getExamId(), categoryId),
                    null,
                    records.getId(),
                    false
            );
            data.setRecordsType(RecordsCollectType.paperRecordRandomFormPaper);
            data.setType(QuestionCollectionType.paper);
            return MapUtil.builder()
                    .put("data", data)
                    .put("mode", records)
                    .build();
        }
        records.setContent(null);
        return records2Result(records, result, userId, category.getIndustryId(), category.getExamId(), categoryId);
    }


    @PostMapping("/generate-special-records")
    @Operation(summary = "生成专项练习试卷记录")
    @EncryptionApi
    @Roles
    @RateLimiterApi(namespace = "exam:safe-generate:", maxRate = 2, recoveryCount = 2, millisecond = 2000, target = RateLimiterApiTarget.user, rejectStrategy = RejectStrategy.throw_exception)
    public Object generateSpecialPaperRecords(
            @RequestParam String industryCode,
            @RequestParam String examId,
            @RequestParam Long categoryId,
            @RequestParam String columnId,
            @RequestParam String examName,
            @RequestParam String categoryName,
            @RequestParam Integer count,
            @RequestParam(defaultValue = "") List<String> chapterIds,
            @ModelAttribute("types") List<SpecialConditionVo.TY> types,
            @RequestParam(defaultValue = "false") Boolean result
    ) throws BizException {
        String userId = userContext.currentUserId();
        var localGroupId = userContext.currentLocalGroupId();
        var ct = userContext.currentTokenData().getClientType();
        if (!categoryService.currentHaveAuth(userContext.currentGroupId(), userContext.currentLocalGroupId(), userId, categoryId, ct)) {
            throw new BizRuntimeException(AclResultCode.noPower);
        }
        var records = PaperRecordsModel.builder()
                .type(PaperRecordType.specialPractice)
                .userId(userId)
                .name(generateName(examName, categoryName, PaperRecordType.specialPractice, userId))
                .examId(examId)
                .categoryId(categoryId)
                .build();
        var inIds = chapterIdsFindAllIds(chapterIds);
        generateQuestionBlock(categoryId, records, types, count, inIds, "专项练习",userContext.loginStatus());
        records.setPassScore((float) (records.getAllScore() * 0.6));
        if (userContext.loginStatus()) {
            records = paperRecordsService.insert(records);
        } else {
            records.setId("public");
            var data = coreQuestionCollectionService.safeProcess(
                    new QuestionRecordsDto(),
                    new CollectionErrorAndCollectRecordDataDto(),
                    coreQuestionCollectionService.clientCollectionData(records, industryCode, examId, categoryId),
                    localGroupId == null ? null : localGroupId.toString(),
                    records.getId(),
                    false
            );
            data.setRecordsType(RecordsCollectType.paperRecordSpecialPractice);
            data.setType(QuestionCollectionType.paper);
            return MapUtil.builder()
                    .put("data", data)
                    .put("mode", records)
                    .build();
        }
        records.setContent(null);
        return records2Result(records, result, userId, industryCode, examId, categoryId);
    }

    @PostMapping("/reset-records")
    @Operation(summary = "重新组卷")
    public Object resetPaperRecords(
            @RequestParam String industryCode,
            @RequestParam String examId,
            @RequestParam Long categoryId,
            @RequestParam String examName,
            @RequestParam String categoryName,
            @RequestParam String recordsId,
            @RequestParam(defaultValue = "false") Boolean result
    ) {
        var userId = userContext.currentUserId();
        var old = paperRecordsService.getById(recordsId);
        if (old == null) {
            throw new BizRuntimeException("记录不存在");
        }
        if (!old.getUserId().equals(userId)) {
            throw new BizRuntimeException(AclResultCode.noPower);
        }
        PaperRecordsModel records;
        if (!old.getHaveFinish()) {
            records = old;
        } else {
            records = PaperRecordsModel.builder()
                    .type(old.getType())
                    .allScore(old.getAllScore())
                    .passScore(old.getPassScore())
                    .userId(userId)
                    .name(generateName(examName, categoryName, old.getType(), userId))
                    .examId(examId)
                    .categoryId(categoryId)
                    .startTime(LocalDateTime.now())
                    .build();
            records.setContentMap(old.getContentMap());
            records = paperRecordsService.insert(records);
        }
        records.setContent(null);
        return records2Result(records, result, userId, industryCode, examId, categoryId);
    }

    @Parameters({
            @Parameter(name = "mode", description = "归类模式 0全部 1章节 2试卷 3其他"),
    })
    @PostMapping("/generate-error-records")
    @Operation(summary = "生成错题试卷")
    public Object generateErrorPaperRecords(
            @RequestParam String industryCode,
            @RequestParam String examId,
            @RequestParam Long categoryId,
            @RequestParam String examName,
            @RequestParam String categoryName,
            @RequestParam Integer count,
            @RequestParam(defaultValue = "0") Integer mode,
            @RequestParam(defaultValue = "true") Boolean result
    ) {
        count = Math.min(count, 200);
        String userId = userContext.currentUserId();
        var records = PaperRecordsModel.builder()
                .type(PaperRecordType.errorCombination)
                .userId(userId)
                .name(generateName(examName, categoryName, PaperRecordType.errorCombination, userId))
                .examId(examId)
                .categoryId(categoryId)
                .build();
        var inIds = errorGeneratePaperCount(userId, industryCode, examId, categoryId, mode);
        generateQuestionBlock(categoryId, records, Collections.emptyList(), count, inIds, "错题组卷", true);
        records.setPassScore((float) (records.getAllScore() * 0.6));
        records = paperRecordsService.insert(records);
        records.setContent(null);
        return records2Result(records, result, userId, industryCode, examId, categoryId);
    }

    @GetMapping("/generate-paper-share")
    @Operation(summary = "生成试卷分享的密文")
    public String generatePaperShare(
            @RequestParam String industryCode,
            @RequestParam String examId,
            @RequestParam Long categoryId,
            @RequestParam String recordsId
    ) {
        String userId = userContext.currentUserId();
        var paperRecord = paperRecordsService.getById(recordsId);
        if (paperRecord == null || !paperRecord.getUserId().equals(userId) || !paperRecord.getHaveFinish()) {
            return null;
        }
        return SecureUtil.aes(MD5Util.MD5Encode(SHARE_AES_KEY).getBytes(StandardCharsets.UTF_8)).encryptHex(String.format("%s,%s", categoryId, recordsId));
    }


    @GetMapping("/paper-records-share-result")
    @Operation(summary = "分享后试卷结果查看")
    @Roles
    public PaperResultVo paperRecordsShareResult(@RequestParam String ciphertext, @RequestParam(defaultValue = "true") Boolean structure) {
        var result = SecureUtil.aes(MD5Util.MD5Encode(SHARE_AES_KEY).getBytes(StandardCharsets.UTF_8)).decryptStr(ciphertext);
        var objs = result.split(",");
        var userId = userContext.currentUserId();
        var categoryId = Long.valueOf(objs[0]);
        var recordsId = objs[1];
        var model = paperRecordsService.getById(recordsId);
        if (!model.getHaveFinish()) {
            throw new BizRuntimeException("试卷还未完成，无法查看");
        }
        var categoryModel = categoryService.getById(categoryId);
        return paperRecordsResult(categoryModel.getIndustryId(), categoryModel.getExamId(), categoryId, recordsId, model.getUserId(), userId, structure);
    }

    /**
     *
     * @param industryCode
     * @param examId
     * @param categoryId
     * @param recordsId
     * @param structure 收藏标识 false不拉取收藏记录
     * @return
     * @throws BizException
     */
    @GetMapping("/paper-records-inline-result")
    @Operation(summary = "内部打开试卷结果查看")
    public PaperResultVo paperRecordsInlineResult(
            @RequestParam String industryCode,
            @RequestParam String examId,
            @RequestParam Long categoryId,
            @RequestParam String recordsId,
            @RequestParam(defaultValue = "true") Boolean structure
    ) throws BizException {
        var userId = userContext.currentUserId();
        boolean free = false;
        String columnId = null;
        var model = paperRecordsService.getById(recordsId);
        if (!model.getUserId().equals(userId) || !model.getHaveFinish()) {
            throw new BizRuntimeException(AclResultCode.noPower);
        }
        if (model.getType().equals(PaperRecordType.paperLibrary) || model.getType().equals(PaperRecordType.randomFormPaper)) {
            ExColumnConfigReModel exColumnConfigReModel = columnConfigReService.selectFirst(Wrappers.<ExColumnConfigReModel>lambdaQuery()
                    .eq(ExColumnConfigReModel::getConfigId, model.getPaperId()));
            free = exColumnConfigReModel.getIsFree();
            columnId = exColumnConfigReModel.getColumnId();
        }
        Long currentGroupId = userContext.currentGroupId();
        Long currentLocalGroupId = userContext.currentLocalGroupId();
        if (!model.getType().equals(PaperRecordType.errorCombination) && !columnService.currentHaveAuth(null, null, userId, categoryId,columnId, userContext.currentTokenData().getClientType()) && !free) {
            throw new BizRuntimeException(ExamResultCode.clientNotOpen);
        }
        return paperRecordsResult(industryCode, examId, categoryId, recordsId, userId, userId, structure);
    }


    @GetMapping("/question-generate-condition")
    @Operation(summary = "专项练习/随机组卷 组卷时返回生成条件")
    @Roles
    public SpecialConditionVo specialConditionResult(@RequestParam Long categoryId, @RequestParam(defaultValue = "") List<String> chapterIds) {
        List<String> inIds = chapterIdsFindAllIds(chapterIds);
        if (CollUtil.isEmpty(chapterIds)) {
            inIds = categoryService.categoryRelationQuestionIds(categoryId);
        }
        if (inIds.isEmpty()) {
            var vo = new SpecialConditionVo();
            vo.setMaxCount(0);
            vo.setTypes(new LinkedList<>());
            return vo;
        }
        return categoryService.categoryGenerateCollectCondition(null, inIds);
    }


    @Parameters({
            @Parameter(name = "mode", description = "归类模式 0全部 1章节 2试卷 3其他"),
    })
    @GetMapping("/error-generate-condition")
    @Operation(summary = "错题组卷返回可以组卷试题数量")
    public long errorGeneratePaperCount(
            @RequestParam String industryCode,
            @RequestParam String examId,
            @RequestParam Long categoryId,
            @RequestParam(defaultValue = "0") Integer mode
    ) {
        String userId = userContext.currentUserId();
        return errorGeneratePaperCount(userId, industryCode, examId, categoryId, mode).size();
    }

    private PaperResultVo paperRecordsResult(String industryCode, String examId, Long categoryId, String recordsId, String userId, String ownerId, boolean structure) {
        var paperRecordsModel = paperRecordsService.selectOne(paperRecordsService.wrappers()
                .eq(BSDEntity::getId, recordsId)
                .eq(PaperRecordsModel::getUserId, userId)
        );
        if (paperRecordsModel == null || !paperRecordsModel.getHaveFinish()) {
            return null;
        }
        CategoryModel categoryModel = categoryService.getById(paperRecordsModel.getCategoryId());
        paperRecordsModel.setCategoryName(categoryModel.getName());
        paperRecordsModel.setExamName(examService.queryName(categoryModel.getExamId()));
        paperRecordsModel.setIndustryId(categoryModel.getIndustryId());
        paperRecordsModel.setIndustryName(industryService.queryName(categoryModel.getIndustryId()));
        var records = recordsGetDataService.getQuestionRecordsResult(paperRecordsModel.getUserId(), industryCode, examId, categoryId, RecordsCollectType.paperRecordType2Type(paperRecordsModel.getType()), recordsId);
        if (structure) {
            paperRecordsModel.setContentMap(null);
            return new PaperResultVo(paperRecordsModel, paperRecordsService.collectionDataStructure(recordsId, industryCode, examId, categoryId), records);
        } else {
            var data = coreQuestionCollectionService.clientCollectionData(paperRecordsModel, industryCode, examId, categoryId);
            var rt = RecordsCollectType.paperRecordType2Type(paperRecordsModel.getType());

            CollectionErrorAndCollectRecordDataDto collectData;
            if (rt == RecordsCollectType.paperRecordPaperLibrary) {
                collectData = recordsGetDataService.getCollectInCollectDataResult(ownerId, industryCode, examId, categoryId, rt, paperRecordsModel.getPaperId());
            } else {
                collectData = userId.equals(ownerId) ? recordsGetDataService.getCollectInCollectDataResult(ownerId, industryCode, examId, categoryId, rt, paperRecordsModel.getId()) : null;
            }
            coreQuestionCollectionService.collectVoForEach(data, q -> {
                var r = records.get(q.getId());
                q.setRecord(r);
                q.setCollect(collectData != null && collectData.getData().containsKey(q.getId()));
            });
            paperRecordsModel.setContentMap(null);
            data.setRecordsType(rt);
            data.setType(QuestionCollectionType.paper);
            return new PaperResultVo(paperRecordsModel, data, null);
        }
    }


    private String generateName(String examName, String categoryName, PaperRecordType type, String userId) {
        var count = 0;
        if (StrUtil.isNotEmpty(userId)) {
            var last = paperRecordsService.selectFirst(paperRecordsService.wrappers()
                    .eq(PaperRecordsModel::getUserId, userId)
                    .eq(PaperRecordsModel::getType, type)
                    .ge(BSDAbsEntity::getCreatedTime, LocalDate.now())
                    .orderByDesc(BSDAbsEntity::getCreatedTime)
            );
            if (last != null) {
                var name = last.getName();
                var x = name.split("-");
                count = Integer.parseInt(x[x.length - 1]);
            }
        }
        return String.format("%s%s-%d", type.getDesc(), TimeUtil.DATE_TIME_FORMATTER_DAY_YMD.format(LocalDate.now()), count + 1);
    }

    private List<String> chapterIdsFindAllIds(List<String> chapterIds) {
        if (CollUtil.isNotEmpty(chapterIds)) {
            return chapterIds.parallelStream()
                    .flatMap(v -> {
                        var owner = chapterService.getById(v);
                        if (null == owner) return Stream.empty();
                        if (owner.getLeaf()) return Stream.of(owner);
                        return chapterService.allChildren(owner, -1, chapterService.wrappers()
                                .select(BaseEntity::getId, ChapterModel::getParentId, ChapterModel::getContentMap)
                                .eq(ChapterModel::getLeaf, true)
                        ).stream();
                    })
                    .filter(ObjectUtil::isNotEmpty)
                    .filter(v -> CollUtil.isNotEmpty(v.getContentMap()))
                    .map(v -> QuestionUtil.getCollectionQuestionAllIds(v.getContent()))
                    .flatMap(Collection::stream)
                    .distinct()
                    .toList();
        }
        return Collections.emptyList();
    }

    private List<String> errorGeneratePaperCount(
            String userId,
            String industryCode,
            String examId,
            Long categoryId,
            int mode
    ) {
        var data = recordsGetDataService.getErrorRecordsResult(userId, industryCode, examId, categoryId);
        return data.getData().keySet().stream().map(k -> {
            var item = data.get(k);
            if (mode == 1) {
                return item.getCt() == RecordsCollectType.chapter ? k : null;
            } else if (mode == 2) {
                return item.getCt().type2CollectType() == QuestionCollectionType.paper ? k : null;
            } else if (mode == 3) {
                return item.getCt().type2CollectType() != QuestionCollectionType.paper && item.getCt().type2CollectType() != QuestionCollectionType.chapter ? k : null;
            }
            return k;
        }).filter(Objects::nonNull).toList();
    }

    private void generateQuestionBlock(Long categoryId, PaperRecordsModel records, List<SpecialConditionVo.TY> types, Integer count, List<String> inIds, String desc, Boolean typeDesc) {
        records.setContent(questionService.generateQuestionBlock(categoryId, types, count, inIds, typeDesc ? item -> String.format("%s-%s", desc, item.getTyName()) : null, allScore -> records.setAllScore(allScore.floatValue())));
        records.setStartTime(LocalDateTime.now());
    }


    private Object records2Result(PaperRecordsModel records, Boolean result, String userId, String industryCode, String examId, Long categoryId) {
        Map<String, Object> res = new HashMap<>();
        res.putAll(result ? Fetch.fetch(paperRecordsService.clientCollectionData(userId, userContext.currentLocalGroupId(), records.getId(), industryCode, examId, categoryId,true))
                .then(v -> {
                    Map<String, Object> map = objectMapper.convertValue(records, Map.class);
                    map.putAll(BeanUtil.beanToMap(v));
                    return map;
                })
                .getData() : objectMapper.convertValue(records, Map.class));
        return res;
    }


}
